﻿/************************************************************************

 This file is part of "10 Second Tim".

 "10 Second Tim" is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 "10 Second Tim" is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with "10 Second Tim".  If not, see <http://www.gnu.org/licenses/>.

************************************************************************/

// All code (c)2013 Scott Bevin. all rights reserved

using System.Collections.Generic;
using LDE.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace LDE
{
	public class Menu
	{
		public delegate void MenuItemSelectedCallback( MenuEntry entry );

		private static float Padding = 15.0f;

		public event MenuItemSelectedCallback OnBack = null;

		public class MenuEntry
		{
			public string Text = "Invalid";

			public event MenuItemSelectedCallback Selected;

			public Vector2 Area;

			public float Alpha = 0.0f;
			public float TargetAlpha = 1.0f;

			public bool Clickable = true;

			public void Call( )
			{
				if ( Selected != null )
					Selected( this );
			}
		}

		public enum Mode
		{
			MouseDriven,
			KeyboardDriven,
		}

		public Mode InputMode = Mode.KeyboardDriven;

		private List<MenuEntry> menuEntries = new List<MenuEntry>( );
		private SpriteFont font;
		private SpriteFont smallFont;

		private SoundEffect hoverSfx;
		private SoundEffect clickSfx;

		private int currentIdx = 0;

		private InputTrigger m_upInput = new InputTrigger( "MenuUp" ).Add( Buttons.DPadUp ).Add( Buttons.LeftThumbstickUp ).Add( Buttons.RightThumbstickUp ).Add( Keys.A ).Add( Keys.Up );
		private InputTrigger m_downInput = new InputTrigger( "MenuDown" ).Add( Buttons.DPadDown ).Add( Buttons.LeftThumbstickDown ).Add( Buttons.RightThumbstickDown ).Add( Keys.S ).Add( Keys.Down );

		private InputTrigger m_mouseAcceptInput = new InputTrigger( "MouseMenuAccept" ).Add( MouseInputs.LeftButton );
		private InputTrigger m_buttonAcceptInput = new InputTrigger( "ButtonMenuAccept" ).Add( Buttons.A ).Add( Keys.Enter ).Add( Keys.Space );

		private InputTrigger m_backInput = new InputTrigger( "Back" ).Add( Buttons.B ).Add( Buttons.Back ).Add( Keys.Back ).Add( Keys.Escape );

		public Menu( SpriteFont font, SpriteFont smallFont, ContentManager content )
		{
			hoverSfx = content.Load<SoundEffect>( "Sfx/MenuHover" );
			clickSfx = content.Load<SoundEffect>( "Sfx/MenuSelect" );

			this.font = font;
			this.smallFont = smallFont;
		}

		public void AddEntry( string text, MenuItemSelectedCallback SelFunc )
		{
			MenuEntry newEntry = new MenuEntry( );
			newEntry.Text = text;
			newEntry.Selected += SelFunc;
			newEntry.Area = font.MeasureString( text );
			newEntry.Alpha = newEntry.TargetAlpha = menuEntries.Count == 0 ? 1.0f : 0.25f;
			menuEntries.Add( newEntry );

			for ( int i = 0; i < menuEntries.Count; ++i )
			{
				if ( menuEntries[ i ].Clickable )
				{
					currentIdx = i;
					break;
				}
			}

			for ( int i = 0; i < menuEntries.Count; ++i )
			{
				menuEntries[ i ].TargetAlpha = menuEntries[ i ].Alpha = menuEntries[ i ].Clickable ? 0.25f : 1.0f;
			}

			menuEntries[ currentIdx ].TargetAlpha = 1.0f;
		}

		public void AddNonClickEntry( string text, MenuItemSelectedCallback SelFunc )
		{
			MenuEntry newEntry = new MenuEntry( );
			newEntry.Text = text;
			newEntry.Selected += SelFunc;
			if ( text.Length == 0 || text == " " )
				newEntry.Area = smallFont.MeasureString( "W" );
			else
				newEntry.Area = smallFont.MeasureString( text );
			newEntry.Clickable = false;
			newEntry.Alpha = newEntry.TargetAlpha = menuEntries.Count == 0 ? 1.0f : 0.25f;
			menuEntries.Add( newEntry );

			for ( int i = 0; i < menuEntries.Count; ++i )
			{
				if ( menuEntries[ i ].Clickable )
				{
					currentIdx = i;
					break;
				}
			}

			for ( int i = 0; i < menuEntries.Count; ++i )
			{
				menuEntries[ i ].TargetAlpha = menuEntries[ i ].Alpha = menuEntries[ i ].Clickable ? 0.25f : 1.0f;
			}

			menuEntries[ currentIdx ].TargetAlpha = 1.0f;
		}

		private MenuEntry lastHoverEntry = null;

		public void Update( float dt, InputHandler input, GraphicsDevice gd )
		{
			if ( input == null )
				return;

			if ( input.IsTapped( m_backInput ) != null )
			{
				if ( OnBack != null )
				{
					OnBack( null );
					return;
				}
			}

			if ( input.HasPointingDevice( ) )
			{
				MenuEntry hoverEntry = GetMenuEntry( gd, input.GetPointerPos( ) );

				if ( hoverEntry != null )
				{
					if ( hoverEntry != lastHoverEntry )
					{
						hoverSfx.Play( );
					}

					lastHoverEntry = hoverEntry;

					for ( int i = 0; i < menuEntries.Count; ++i )
					{
						menuEntries[ i ].TargetAlpha = 0.25f;
					}

					hoverEntry.TargetAlpha = 1.0f;
				}
				else
				{
					for ( int i = 0; i < menuEntries.Count; ++i )
					{
						menuEntries[ i ].TargetAlpha = menuEntries[ i ].Clickable ? 1.0f : 1.0f;
					}
				}

				if ( hoverEntry != null && input.IsTapped( m_mouseAcceptInput ) != null )
				{
					hoverEntry.Call( );
					clickSfx.Play( );
				}
			}

			{
				if ( input.IsTapped( m_downInput ) != null && currentIdx < menuEntries.Count - 1 )
				{
					for ( int i = currentIdx + 1; i < menuEntries.Count; ++i )
					{
						if ( menuEntries[ i ].Clickable )
						{
							currentIdx = i;
							hoverSfx.Play( );
							break;
						}
					}

					for ( int i = 0; i < menuEntries.Count; ++i )
					{
						menuEntries[ i ].TargetAlpha = menuEntries[ i ].Clickable ? 0.25f : 1.0f;
					}

					menuEntries[ currentIdx ].TargetAlpha = 1.0f;
				}

				if ( input.IsTapped( m_upInput ) != null && currentIdx > 0 )
				{
					for ( int i = currentIdx - 1; i >= 0; --i )
					{
						if ( menuEntries[ i ].Clickable )
						{
							currentIdx = i;
							hoverSfx.Play( );
							break;
						}
					}

					for ( int i = 0; i < menuEntries.Count; ++i )
					{
						menuEntries[ i ].TargetAlpha = menuEntries[ i ].Clickable ? 0.25f : 1.0f;
					}

					menuEntries[ currentIdx ].TargetAlpha = 1.0f;
				}

				if ( input.IsTapped( m_buttonAcceptInput ) != null )
				{
					menuEntries[ currentIdx ].Call( );
					clickSfx.Play( );
				}
			}

			for ( int i = 0; i < menuEntries.Count; ++i )
			{
				menuEntries[ i ].Alpha = MathHelper.Lerp( menuEntries[ i ].Alpha, menuEntries[ i ].TargetAlpha, dt * 3.0f );
			}
		}

		private MenuEntry GetMenuEntry( GraphicsDevice gd, Point screenPos )
		{
			Vector2 basePosition = new Vector2( ( (float)gd.PresentationParameters.BackBufferWidth ) * 0.5f,
				( (float)gd.PresentationParameters.BackBufferHeight ) * 0.5f );

			foreach ( MenuEntry me in menuEntries )
			{
				if ( me.Clickable )
				{
					if ( (float)screenPos.X <= basePosition.X + ( me.Area.X * 0.5f ) &&
						(float)screenPos.X >= basePosition.X - ( me.Area.X * 0.5f ) &&
						(float)screenPos.Y <= basePosition.Y + ( me.Area.Y * 0.5f ) &&
						(float)screenPos.Y >= basePosition.Y - ( me.Area.Y * 0.5f ) )
					{
						return me;
					}
				}

				basePosition.Y += ( me.Area.Y * 0.5f ) + Padding;
			}

			return null;
		}

		public void OnClick( GraphicsDevice gd, Vector2 screenPos )
		{
			//   MenuEntry me = GetMenuEntry(gd, screenPos);

			//     if (me != null && me.Selected != null)
			//          me.Selected(me);
		}

		public void Render( GraphicsDevice gd, SpriteBatch sprites )
		{
			Vector2 basePosition = new Vector2( ( (float)gd.PresentationParameters.BackBufferWidth ) * 0.5f,
				( (float)gd.PresentationParameters.BackBufferHeight ) * 0.5f );

			sprites.Begin( SpriteSortMode.Deferred, BlendState.NonPremultiplied );

			foreach ( MenuEntry me in menuEntries )
			{
				sprites.DrawString(
					me.Clickable ? font : smallFont,
					me.Text,
					basePosition,
					( me == menuEntries[ currentIdx ] ? Color.Red : Color.Black ) * me.Alpha,
					0.0f,
					me.Area * 0.5f,
					1.0f,
					SpriteEffects.None,
					0.0f );

				basePosition.Y += ( me.Area.Y * 0.5f ) + Padding;
			}

			sprites.End( );
		}
	}
}